package cn.walls1717.wallsmall.cart.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.walls1717.wallsmall.cart.feign.ProductFeignService;
import cn.walls1717.wallsmall.cart.service.CartService;
import cn.walls1717.wallsmall.cart.to.UserInfoTO;
import cn.walls1717.wallsmall.cart.util.UserHandler;
import cn.walls1717.wallsmall.cart.vo.CartItemVO;
import cn.walls1717.wallsmall.cart.vo.CartVO;
import cn.walls1717.wallsmall.cart.vo.SkuInfoVo;
import cn.walls1717.wallsmall.common.utils.R;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static cn.walls1717.wallsmall.common.constant.cart.CartConstant.CART_PREFIX;

/**
 * @author walls1717
 * @since 2022/11/14 21:42
 **/
@Service
@Slf4j
public class CartServiceImpl implements CartService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private ProductFeignService productFeignService;
    @Resource
    private ThreadPoolExecutor executor;

    /**
     * 保存商品到购物车
     *
     * @param skuId skuId
     * @param num 商品数量
     */
    @Override
    public void saveCartItem(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        String result = (String)cartOps.get(skuId.toString());
        if (!StrUtil.isEmpty(result)) {
            // 购物车中已经有此商品，修改数量即可。
            CartItemVO redisCartItemVO = JSON.parseObject(result, CartItemVO.class);
            redisCartItemVO.setCount(redisCartItemVO.getCount() + num);
            cartOps.put(skuId.toString(), JSON.toJSONString(redisCartItemVO));
            return;
        }

        CartItemVO cartItemVO = new CartItemVO();
        CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
            R r = productFeignService.getSkuInfo(skuId);
            SkuInfoVo skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {});
            cartItemVO.setImage(skuInfo.getSkuDefaultImg());
            cartItemVO.setTitle(skuInfo.getSkuTitle());
            cartItemVO.setPrice(skuInfo.getPrice());
        }, executor);

        CompletableFuture<Void> saleAttrValuesFuture = CompletableFuture.runAsync(() -> {
            List<String> values = productFeignService.listSkuSaleAttrValues(skuId);
            cartItemVO.setSkuAttr(values);
        }, executor);

        cartItemVO.setCheck(true);
        cartItemVO.setCount(1);
        cartItemVO.setSkuId(skuId);

        CompletableFuture.allOf(skuInfoFuture, saleAttrValuesFuture).get();

        cartOps.put(skuId.toString(), JSON.toJSONString(cartItemVO));

    }

    /**
     * 根据skuId获取购物车中指定购物项
     *
     * @param skuId skuId
     * @return 购物项
     */
    @Override
    public CartItemVO getCartItemBySkuId(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String cartJson = (String)cartOps.get(skuId.toString());
        return JSON.parseObject(cartJson, CartItemVO.class);
    }

    /**
     * 获取购物车信息
     *
     * @return 购物车信息
     */
    @Override
    public CartVO getCart() throws ExecutionException, InterruptedException {
        UserInfoTO userInfo = UserHandler.get();
        CartVO cartVO = new CartVO();
        String userKey = CART_PREFIX + userInfo.getUserKey();
        List<CartItemVO> tempCartItems = listCartItemsByKey(userKey);

        if (userInfo.getUserId() == null) {
            cartVO.setItems(tempCartItems);
        } else {
            if (tempCartItems != null && tempCartItems.size() > 0) {
                for (CartItemVO loginCartItem : tempCartItems) {
                    saveCartItem(loginCartItem.getSkuId(), loginCartItem.getCount());
                }
                removeCartByKey(userKey);
            }
            List<CartItemVO> loginCartItems = listCartItemsByKey(CART_PREFIX + userInfo.getUserId());
            cartVO.setItems(loginCartItems);
        }

        return cartVO;
    }

    /**
     * 根据指定key清空购物车
     *
     * @param key key
     */
    @Override
    public void removeCartByKey(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 根据skuId更新购物车中购物项的选中状态
     *
     * @param skuId skuId
     * @param check 选中状态[0,1]
     */
    @Override
    public void updateCheckBySkuId(Long skuId, Integer check) {
        CartItemVO cartItem = getCartItemBySkuId(skuId);
        cartItem.setCheck(check == 1);
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
    }

    /**
     * 根据skuId更新购物车中购物项的数量
     *
     * @param skuId skuId
     * @param num 数量
     */
    @Override
    public void updateCountBySkuId(Long skuId, Integer num) {
        CartItemVO cartItem = getCartItemBySkuId(skuId);
        cartItem.setCount(num);
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
    }

    /**
     * 根据skuId删除购物项
     *
     * @param skuId skuId
     */
    @Override
    public void removeCartItemBySkuId(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.delete(skuId.toString());
    }

    /**
     * 获取当前用户购物车中的所有购物项
     *
     * @return 购物项集合
     */
    @Override
    public List<CartItemVO> listCurrentUserCartItems() {
        UserInfoTO loginUser = UserHandler.get();
        Long userId = loginUser.getUserId();
        // 用户没有登录直接返回空
        if (userId == null) {
            return null;
        }
        // 没有购物项，返回空。
        List<CartItemVO> cartItems = listCartItemsByKey(CART_PREFIX + userId);
        if (cartItems == null) {
            return null;
        }
        // 返回选中的购物项，并设置最新价格。
        return cartItems.stream().filter(CartItemVO::getCheck).peek(item -> {
            BigDecimal price = productFeignService.getPriceBySkuId(item.getSkuId());
            item.setPrice(price);
        }).collect(Collectors.toList());
    }

    @Nullable
    private List<CartItemVO> listCartItemsByKey(String key) {
        BoundHashOperations<String, Object, Object> cartOps = stringRedisTemplate.boundHashOps(key);
        List<Object> values = cartOps.values();
        List<CartItemVO> cartItems = null;
        if (values != null && values.size() > 0) {
            cartItems = values.stream().map(value -> {
                String cartItemStr = (String)value;
                return JSON.parseObject(cartItemStr, CartItemVO.class);
            }).collect(Collectors.toList());
        }
        return cartItems;
    }

    /**
     * 获取需要操作的购物车
     *
     * @return 被操作购物车
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
        UserInfoTO userInfoTO = UserHandler.get();
        String cartKey = "";
        if (userInfoTO.getUserId() != null) {
            cartKey = CART_PREFIX + userInfoTO.getUserId();
        } else {
            cartKey = CART_PREFIX + userInfoTO.getUserKey();
        }
        return stringRedisTemplate.boundHashOps(cartKey);
    }
}
